package com.sxkiler.demo.utils;

import com.google.common.base.CaseFormat;
import com.google.common.collect.ImmutableSet;
import com.sxkiler.demo.biz.dao.BaseDao;
import com.sxkiler.demo.rpc.client.RpcClientFactory;
import com.sxkiler.demo.rpc.model.RpcReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class BeanUtils {
    private static Logger logger = LoggerFactory.getLogger(BeanUtils.class);
    private static Map<String,Object> beanCache = new HashMap<String,Object>();

    /**
     * 将初始化的对象放到缓存
     * @param value
     */
    public static void addBean(Object value){
        beanCache.put(value.getClass().getName(),value);
    }
    public static void addBean(Class beanClass,Object value){
        beanCache.put(beanClass.getName(),value);
    }
    public static void addBean(String beanName,Object value){
        beanCache.put(beanName,value);
    }
    public static <T> T getApplicationBean(Class<T> beanClass){
        return getApplicationBean(beanClass.getName());
    }
    public static <T> T getApplicationBean(String beanName){
        Object obj = beanCache.get(beanName);
        try{
            T res = (T)obj;
            return res;
        }catch(Exception e){
            logger.error("强转失败,类型不匹配,{}",beanName);
            return null;
        }
    }
    public static <T> T buildBean(Class<T> testService,Object ... values) throws Exception {
        return buildBean(testService.getSimpleName(),testService,values);
    }
    public static <T> T buildBean(String serviceName,Class<T> testService,Object ... values) throws Exception {
        T t = null;
        try {
            //如果已经初始化之后,就不需要重新初始化了
            if(((t = getApplicationBean(testService))!=null)
                    ||((t = getApplicationBean(testService.getName()))!=null)
                    ||((t = getApplicationBean(testService.getSimpleName()))!=null)){
                return t;
            }
            t = testService.newInstance();
            Field[] fields = testService.getDeclaredFields();
            for(Field field:fields){
                if(
                        Map.class.isAssignableFrom(field.getType())
                                || List.class.isAssignableFrom(field.getType())
                                || String.class.isAssignableFrom(field.getType())
                                || Integer.class.isAssignableFrom(field.getType())
                                || Logger.class.isAssignableFrom(field.getType())
                ){
                    continue;
                }
                Class fieldClass;
                try {
                    fieldClass = Class.forName(field.getGenericType().getTypeName());
                } catch (ClassNotFoundException e) {
                    // service实现中存在int double时经常报错
                    e.printStackTrace();
                    continue;
                }
                Annotation[] annotations = field.getAnnotations();
                if(annotations!=null && annotations.length > 0 ){
                    Annotation annotation = annotations[0];
                    if(BaseDao.class.isAssignableFrom(fieldClass)
                            ||field.getGenericType().getTypeName().endsWith("Dao")
                            ||field.getGenericType().getTypeName().endsWith("Mapper")){
                        Object daoClass= DaoContext.getDao(fieldClass);
                        if(daoClass!=null){
                            field.setAccessible(true);
                            field.set(t,daoClass);
                        }
                    }else if(annotation instanceof Resource){
                        if(values != null && values.length > 0 ){
                            for(Object obj:values){
                                if(obj!=null && fieldClass.isInstance(obj)){
                                    field.setAccessible(true);
                                    field.set(t,obj);
                                }
                            }
                        }else{
                            try{
                                Object obj = getApplicationBean(fieldClass);
                                field.setAccessible(true);
                                field.set(t,obj);
                            }catch(Exception ex){
                                ex.printStackTrace();
                            }
                        }
                    }
                    else if(annotation instanceof RpcReference){
                        RpcClientFactory factory = (RpcClientFactory)Class.forName("com.sxkiler.demo.rpc.client.RpcClientProxy").newInstance();
                        Object clientService = factory.getBean(fieldClass);
                        field.setAccessible(true);
                        field.set(t,clientService);
                    }

                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        }
        BeanUtils.addBean(serviceName,t);
        return t;
    }

    /**
     * 获取属性的get方法
     * @param propertyName
     * @return
     */
    public static String getSetterMethodName(String propertyName) {
        return propertyName.contains("-") ? CaseFormat.LOWER_HYPHEN.to(CaseFormat.LOWER_CAMEL, "set-" + propertyName) : "set" + String.valueOf(propertyName.charAt(0)).toUpperCase() + propertyName.substring(1, propertyName.length());
    }
    /**
     * 调用目标对象的set方法
     */
    private static final Collection<Class<?>> GENERAL_CLASS_TYPE = ImmutableSet.of(Boolean.TYPE, Boolean.class, Integer.TYPE, Integer.class, Long.TYPE, Long.class, String.class);
    public static void callSetterMethod(DataSource dataSource, String methodName, String setterValue) {
        boolean successSet = false;
        for (Class<?> each : GENERAL_CLASS_TYPE) {
            try {
                Method method = dataSource.getClass().getMethod(methodName, each);
                if (Boolean.TYPE == each || Boolean.class == each) {
                    method.invoke(dataSource, Boolean.valueOf(setterValue));
                } else if (Integer.TYPE == each || Integer.class == each) {
                    method.invoke(dataSource, Integer.parseInt(setterValue));
                } else if (Long.TYPE == each || Long.class == each) {
                    method.invoke(dataSource, Long.parseLong(setterValue));
                } else {
                    method.invoke(dataSource, setterValue);
                }

                return;
            } catch (ReflectiveOperationException var6) {
            }
        }
    }
}
